package com.ruyiadmin.springboot.common.aspects.system;

import cn.hutool.core.util.ArrayUtil;
import com.alibaba.fastjson.JSON;
import com.ruyiadmin.springboot.common.annotations.system.Permission;
import com.ruyiadmin.springboot.common.beans.system.SystemCacheConfig;
import com.ruyiadmin.springboot.common.beans.system.SystemRedisConfig;
import com.ruyiadmin.springboot.common.core.business.enums.MenuType;
import com.ruyiadmin.springboot.common.core.business.enums.YesNo;
import com.ruyiadmin.springboot.common.components.core.RuYiSessionContext;
import com.ruyiadmin.springboot.common.components.core.RuYiRedisComponent;
import com.ruyiadmin.springboot.common.exceptions.RuYiAdminCustomException;
import com.ruyiadmin.springboot.domain.dto.system.SysMenuDTO;
import com.ruyiadmin.springboot.domain.dto.system.SysUserDTO;
import com.ruyiadmin.springboot.domain.entity.system.SysRoleMenu;
import com.ruyiadmin.springboot.domain.entity.system.SysRoleUser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 操作鉴权切面定义
 * </p>
 *
 * @author RuYiAdmin
 * @since 2022-07-20
 */
@Aspect
@Slf4j
@Component
@RequiredArgsConstructor
@EnableConfigurationProperties({SystemRedisConfig.class})
public class OperationPermissionAspect {

    //region 切面私有属性

    private final RuYiRedisComponent redisUtils;
    private final SystemCacheConfig systemCacheConfig;
    private final RuYiSessionContext sessionContext;

    //endregion

    //region 操作鉴权切面入点

    @Pointcut("@annotation(com.ruyiadmin.springboot.common.annotations.system.Permission)")
    public void permissionPoint() {
    }

    //endregion

    //region 操作动作鉴权

    @Around("permissionPoint()")
    public Object hasPermission(ProceedingJoinPoint joinPoint) throws Throwable {
        //返回数据
        Object proceed = null;

        //方法签名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        //获取方法
        Method aimMethod = methodSignature.getMethod();

        //获取 @OperationPermission 的值
        String permissions = aimMethod.getAnnotation(Permission.class).permission();

        if (StringUtils.isEmpty(permissions)) {
            throw new RuYiAdminCustomException("permission can not be null");
        }

        SysUserDTO user = sessionContext.getCurrentUserInfo();
        if (user == null) {
            throw new RuYiAdminCustomException("token is invalid");
        }

        //放行超级用户
        if (user.getIsSupperAdmin() == YesNo.YES.ordinal()) {
            //允许通过，调用的方法可以往下运行
            return joinPoint.proceed();
        }

        String[] permissionArray = permissions.split(",");

        //region 获取用户角色

        Object value = this.redisUtils.get(systemCacheConfig.getRoleAndUserCacheName());
        List<SysRoleUser> roleUsers = JSON.parseArray(value.toString(), SysRoleUser.class);

        roleUsers = roleUsers.stream().
                filter(t -> t.getIsdel() == 0).
                filter(t -> t.getUserId().equals(user.getId())).
                collect(Collectors.toList());

        //endregion

        //region  获取角色菜单

        value = this.redisUtils.get(systemCacheConfig.getRoleAndMenuCacheName());
        List<SysRoleMenu> roleMenuList = JSON.parseArray(value.toString(), SysRoleMenu.class);

        List<SysRoleMenu> listRoleMenu = new ArrayList<>();
        for (SysRoleUser item : roleUsers) {
            List<SysRoleMenu> roleMenus = roleMenuList.stream().
                    filter(t -> t.getIsdel() == 0).
                    filter(t -> t.getRoleId().equals(item.getRoleId())).
                    collect(Collectors.toList());
            listRoleMenu.addAll(roleMenus);
        }

        //endregion

        //region 获取系统菜单

        value = this.redisUtils.get(systemCacheConfig.getMenuCacheName());
        List<SysMenuDTO> menus = JSON.parseArray(value.toString(), SysMenuDTO.class);

        //视图按钮
        menus = menus.stream().filter(t -> t.getIsdel() == 0).
                filter(t -> t.getMenuType() == MenuType.Button.ordinal() ||
                        t.getMenuType() == MenuType.View.ordinal()).
                collect(Collectors.toList());

        //endregion

        //region 操作权限判断

        boolean result = false;

        for (SysRoleMenu item : listRoleMenu) {
            List<SysMenuDTO> menuTemps = menus.stream().
                    filter(t -> t.getId().equals(item.getMenuId())).
                    collect(Collectors.toList());
            SysMenuDTO menu = menuTemps.size() > 0 ? menuTemps.get(0) : null;
            if (menu != null && !StringUtils.isEmpty(menu.getCode())) {
                //是否包含权限组
                if (ArrayUtil.containsAny(permissionArray, menu.getCode())) {
                    result = true;
                    break;
                }
            }
        }

        if (!result) {
            throw new RuYiAdminCustomException("access denied");
        }

        //endregion

        //允许通过，调用的方法可以往下运行
        proceed = joinPoint.proceed();

        return proceed;
    }

    //endregion

}
